﻿using System;
using System.Collections.Generic;
using ShuttleBus.AdminMessages;
using ShuttleBus.Subscriptions;

namespace ShuttleBus
{
    public partial class Bus : IBus
    {
        private readonly IHandleMessages _dynamicMessageHandler;
        private readonly ISubscriptionRepository _subscriptionRepository;
        private readonly ICreateMessages _factory;
        private readonly ITransport _transport;

        internal Bus(ITransport transport, ISubscriptionRepository subscriptionRepository, ICreateMessages factory, IHandleMessages dynamicMessageHandler)
        {
            _transport = transport;
            _subscriptionRepository = subscriptionRepository;
            _factory = factory;
            _dynamicMessageHandler = dynamicMessageHandler;
        }

        internal Bus(ITransport transport, ISubscriptionRepository subscriptionRepository, ICreateMessages factory)
            : this(transport, subscriptionRepository, factory, new DynamicMessageHandler())
        {
        }

        public void Start()
        {
            _transport.MessageArrived += TransportMessageArrived;
            _transport.HandleAdministrativeMessage += AdministrativeMessageArrived;
            _transport.Start();
        }

        public void Stop()
        {
            _transport.Stop();
        }

        public void Send(Uri location, Message message)
        {
            var transportMessage = _factory.Create(message);
            _transport.Send(transportMessage, new Subscription(location, message.GetType()));
        }

        public void Publish(Message message)
        {
            var transportMessage = _factory.Create(message);

            foreach (var subscription in _subscriptionRepository.GetSubscribersOf(message))
            {
                _transport.Send(transportMessage, subscription);
            }
        }

        public void Publish(IEnumerable<Message> events)
        {
            foreach (var message in events)
            {
                Publish(message);
            }
        }

        public void TransportMessageArrived(object obj)
        {
            _dynamicMessageHandler.HandleMessage(obj);
        }

        private void AdministrativeMessageArrived(SubscriptionMessage obj)
        {
            if (obj is AddSubscriptionMessage)
            {
                _subscriptionRepository.Add(obj as AddSubscriptionMessage);
            }
        }
    }
}